#include <Windows.h>
#include <stdio.h>
#include <stdint.h>
#include <winnt.h>
#include <tchar.h>
#include <strsafe.h>
#include <sddl.h>
#include <AccCtrl.h>
#include <Aclapi.h>

BOOL CreateLowProcess(const wchar_t *ImagePath, uint32_t dwPidParam) {
    BOOL                  fRet;
    HANDLE                hToken = NULL;
    HANDLE                hNewToken = NULL;
    PSID                  pIntegritySid = NULL;
    TOKEN_MANDATORY_LABEL TIL = { 0 };
    PROCESS_INFORMATION   ProcInfo = { 0 };
    STARTUPINFO           StartupInfo = { 0 };
    wchar_t CommandLine[MAX_PATH + 1] = { 0 };

    //wcscpy_s(CommandLine, MAX_PATH + 1, ImagePath);
    _snwprintf_s(CommandLine, sizeof(CommandLine), MAX_PATH + 1, L"%ws %d", ImagePath, dwPidParam);

    //wchar_t wszIntegritySid[20] = L"S-1-16-1024";
    wchar_t wszIntegritySid[20] = L"S-1-16-4096"; // Low integrity SID

    fRet = OpenProcessToken(GetCurrentProcess(),
        TOKEN_DUPLICATE |
        TOKEN_ADJUST_DEFAULT |
        TOKEN_QUERY |
        TOKEN_ASSIGN_PRIMARY,
        &hToken);

    if (!fRet)
    {
        printf("... OpenProcessToken failed\r\n");
        goto CleanExit;
    }

    fRet = DuplicateTokenEx(hToken,
        0,
        NULL,
        SecurityImpersonation,
        TokenPrimary,
        &hNewToken);

    if (!fRet)
    {
        printf("... DuplicateTokenEx failed\r\n");
        goto CleanExit;
    }

    fRet = ConvertStringSidToSidW(wszIntegritySid, &pIntegritySid);

    if (!fRet)
    {
        printf("... ConvertStringSidToSidW failed\r\n");
        goto CleanExit;
    }

    TIL.Label.Attributes = SE_GROUP_INTEGRITY;
    TIL.Label.Sid = pIntegritySid;

    //
    // Set the process integrity level
    //

    fRet = SetTokenInformation(hNewToken,
        TokenIntegrityLevel,
        &TIL,
        sizeof(TOKEN_MANDATORY_LABEL) + GetLengthSid(pIntegritySid));

    if (!fRet)
    {
        printf("... SetTokenInformation failed\r\n");
        goto CleanExit;
    }

    //
    // Create the new process at Low integrity
    //

    StartupInfo.cb = sizeof(StartupInfo);

    fRet = CreateProcessAsUserW(hNewToken,
        NULL,
        CommandLine,
        NULL,
        NULL,
        FALSE,
        0,//DETACHED_PROCESS,
        NULL,
        NULL,
        &StartupInfo,
        &ProcInfo);

    if (!fRet)
    {
        printf("... CreateProcessAsUserW failed\r\n");
        goto CleanExit;
    }

CleanExit:

    if (ProcInfo.hProcess != NULL)
    {
        CloseHandle(ProcInfo.hProcess);
    }

    if (ProcInfo.hThread != NULL)
    {
        CloseHandle(ProcInfo.hThread);
    }

    LocalFree(pIntegritySid);

    if (hNewToken != NULL)
    {
        CloseHandle(hNewToken);
    }

    if (hToken != NULL)
    {
        CloseHandle(hToken);
    }

    return fRet;
}
/*
DWORD GetSID(const wchar_t *lptszUserName, PSID *pSid, DWORD *pdwSize)
{
    DWORD dwError;
    LPTSTR lptszDomainName;
    DWORD dwDomainNameLen;
    SID_NAME_USE snu;
    LPTSTR lptszSid;
    BOOL bRet;

    dwError = ERROR_SUCCESS;

    *pdwSize = 0;
    dwDomainNameLen = 0;
    lptszDomainName = NULL;
    bRet = LookupAccountName(NULL, lptszUserName, NULL, pdwSize, NULL, &dwDomainNameLen, &snu);
    dwError = GetLastError();
    *pSid = new BYTE[*pdwSize];
    lptszDomainName = new TCHAR[dwDomainNameLen + 1];
    SecureZeroMemory(lptszDomainName, sizeof(TCHAR) * (dwDomainNameLen + 1));
    SecureZeroMemory(*pSid, *pdwSize);
    bRet = LookupAccountName(NULL, lptszUserName, *pSid, pdwSize, lptszDomainName, &dwDomainNameLen, &snu);
    dwError = GetLastError();

    delete[] lptszDomainName;
    return dwError;
}

void CreateLowProcess(const wchar_t *ImagePath)
{
    DWORD dwError;

    dwError = ERROR_SUCCESS;
    HANDLE hProcessToken;
    HANDLE hNewProcessToken;
    BOOL bRet;
    bRet = OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_QUERY, &hProcessToken);

    PSID sidAdministrator;
    DWORD dwSize;
    dwSize = 0;
    dwError = GetSID(L"Administrator", &sidAdministrator, &dwSize);

    SID_AND_ATTRIBUTES sidAttr = { sidAdministrator, 0 };

    LUID luid;
    bRet = LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &luid);
    LUID_AND_ATTRIBUTES luidAttr = { luid, 0 };

    STARTUPINFO si;
    SecureZeroMemory(&si, sizeof(STARTUPINFO));
    si.cb = sizeof(STARTUPINFO);
    PROCESS_INFORMATION pi;

    bRet = CreateRestrictedToken(hProcessToken, 0,
        1, &sidAttr,
        1, &luidAttr,
        0, NULL, &hNewProcessToken);

    bRet = CreateProcessAsUser(hNewProcessToken, ImagePath, NULL,
        NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
    LocalFree(sidAdministrator);
    CloseHandle(hProcessToken);
}*/

int32_t wmain(int32_t nArgc, const wchar_t* pArgv[]) {
    CreateLowProcess(pArgv[1], _wtoi(pArgv[2]));
    return 0;
}
